﻿using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Extended.NetRouting.Test
{
    [TestClass]
    public class MainTest
    {
        private static readonly MainTestNode nodeA = new MainTestNode("A");
        private static readonly MainTestNode nodeB = new MainTestNode("B");
        private static readonly MainTestNode nodeC = new MainTestNode("C");
        private static readonly MainTestNode nodeD = new MainTestNode("D");
        private static readonly MainTestNode nodeE = new MainTestNode("E");
        private static readonly MainTestNode nodeF = new MainTestNode("F");
        private static readonly MainTestNode nodeG = new MainTestNode("G");
        private static readonly MainTestNode nodeH = new MainTestNode("H");
        private static readonly MainTestNode nodeI = new MainTestNode("I");
        
        private static IRouteProvider routeProvider;

        [ClassInitialize]
        public static void MyClassInitialize(TestContext testContext)
        {
            // -------------------------------------------------------------------------------------
            // see attached diagram (.vsd or .png) for graphical representation of this sample network
            // -------------------------------------------------------------------------------------
            var route01 = new Route(nodeA, nodeB);
            var route02 = new Route(nodeB, nodeC);
            var route03 = new Route(nodeC, nodeH);
            var route04 = new Route(nodeC, nodeD);
            var route05 = new Route(nodeD, nodeE);
            var route06 = new Route(nodeI, nodeF);
            var route07 = new Route(nodeF, nodeG);
            var route08 = new Route(nodeG, nodeI);
            var route09 = new Route(nodeC, nodeB);
            var route10 = new Route(nodeD, nodeC);
            var route11 = new Route(nodeB, nodeI, length: 5, tag: "long");  // there are two routes from B to I so we have to tag this one
            var route12 = new Route(nodeB, nodeI, length: 1, tag: "short"); // there are two routes from B to I so we have to tag this one
            var route13 = new Route(nodeI, nodeE, 6);       // route with length equal to 6
            var route14 = new Route(nodeG, nodeG);          // route that begins and ends to the same spot 

            var memoryRouteProvider = new MemoryRouteProvider();
            memoryRouteProvider.AddRange(new[] { route01, route02, route03, route04, route05, route06, route07, route08, route09, route10, route11, route12, route13, route14 });
            routeProvider = memoryRouteProvider;
        }

        [TestMethod]
        public void ComplexTest()
        {
            // get all paths
            var paths = routeProvider.GetPaths(nodeA, nodeE).ToList();
            Assert.AreEqual(3, paths.Count);

#if DEBUG
            Assert.AreEqual(10, Network.RouteVisitor.ExhaustedPaths);
#endif

            // debug write results
            foreach (var path in paths) Debug.WriteLine(path.ToNodeString(".") + " Length: " + path.Length);

            // get shortest path
            var shortestPath = routeProvider.GetShortestPath(nodeA, nodeE);
            Assert.AreEqual(4, shortestPath.Length);
            Assert.AreEqual("A.B.C.D.E", shortestPath.ToNodeString("."));

#if DEBUG
            // assert the number of paths searched. number when using GetShortestPath should be smaller
            // than when using GetPaths, which searches all available paths
            Assert.AreEqual(6, Network.RouteVisitor.ExhaustedPaths);
#endif
        }

        [TestMethod]
        public void BidirectionalPath()
        {
            var shortestPath = routeProvider.GetShortestPath(nodeD, nodeB);
            Assert.AreEqual(2, shortestPath.Length);
            Assert.AreEqual("D.C.B", shortestPath.ToNodeString("."));

            shortestPath = routeProvider.GetShortestPath(nodeB, nodeD);
            Assert.AreEqual(2, shortestPath.Length);
            Assert.AreEqual("B.C.D", shortestPath.ToNodeString("."));
        }

        [TestMethod]
        public void SingleRoutePathTest()
        {
            var paths = routeProvider.GetPaths(nodeI, nodeF).ToList();
            Assert.AreEqual(1, paths.Count);

            var shortestPath = routeProvider.GetShortestPath(nodeI, nodeF);
            Assert.AreEqual("I.F", shortestPath.ToNodeString("."));
        }

        [TestMethod]
        public void PathInLoop()
        {
            var paths = routeProvider.GetPaths(nodeI, nodeG).ToList();
            Assert.AreEqual(1, paths.Count);

            var shortestPath = routeProvider.GetShortestPath(nodeI, nodeG);
            Assert.AreEqual("I.F.G", shortestPath.ToNodeString("."));
        }

        [TestMethod]
        public void NoPathTest()
        {
            var paths = routeProvider.GetPaths(nodeI, nodeH).ToList();
            Assert.AreEqual(0, paths.Count);

            var shortestPath = routeProvider.GetShortestPath(nodeI, nodeH);
            Assert.AreEqual(null, shortestPath);
        }

        [TestMethod]
        public void PathToStringTest()
        {
            // prepare a test path
            var path = new Path();
            path.Add(new Route(nodeA, nodeB));
            path.Add(new Route(nodeB, nodeC));
            path.Add(new Route(nodeC, nodeD));
            path.Add(new Route(nodeD, nodeE));

            // assert
            Assert.AreEqual("A.B.C.D.E", path.ToNodeString(".", true, true));
            Assert.AreEqual("A.B.C.D", path.ToNodeString(".", true, false));
            Assert.AreEqual("B.C.D.E", path.ToNodeString(".", false, true));
            Assert.AreEqual("B.C.D", path.ToNodeString(".", false, false));
        }
    }
}
